(function(){

var eventNames = 'ontouchstart' in document ? {
	mousedown: 'touchstart',
	mousemove: 'touchmove',
	mouseup: 'touchend'
} : {
	mousedown: 'mousedown',
	mousemove: 'mousemove',
	mouseup: 'mouseup'
};

function $Q(q){
	return typeof(q) == 'string' ? document.querySelector(q) : q;
}
function fun(v){
	return typeof(v) == 'function';
}

function addEvent(el, name, callback, useCapture){
	if(name in eventNames){
		name = eventNames[name];
	}
	el.addEventListener(name, callback, useCapture);
}
function removeEvent(el, name, callback){
	if(name in eventNames){
		name = eventNames[name];
	}
	el.removeEventListener(name, callback);
}
function getSingleEvent(e){
	if('changedTouches' in e){
		return e.changedTouches[0];
	}
	return e;
}
function getSingleEventPostion(e){
	e = getSingleEvent(e);
	return [e.clientX, e.clientY];
}
function getSingleEventPostionINT(e){
	e = getSingleEvent(e);
	return [e.clientX >> 0, e.clientY >> 0];
}

function transToPoint(i, w, h){
	return [i % w, i / w >> 0];
}

function mosaicColorTest(data, w, x, y, gridSize, rgba){

	rgba = rgba || 3;
	var _x = 0, _y = 0, _c = 0;
	for(var i = 0; i < gridSize; i++){
		for(var j = 0; j < gridSize; j++){
			idx = (i + x) + (y + j) * w;
			if(data[idx * 4 + rgba] > 0){
				return true;
				_x += i;
				_y += j;
				_c += 1;
			}
		}
	}
	// console.log(_c, _x, _y, gridSize)
	return _c > 0;
}

function cleanImageData(data, w){
	var d = [];
	data.forEach(function(a, i, D){
		if(i % 4 == 0 && D[i + 3] > 0){
			d.push(i)
		}
	});
	var xs = [];
	var ys = [];
	var coords = d.map(function(a, i){
		a = a / 4;
		var x = a % w;
		var y = (a / w) >> 0;
		if(xs.indexOf(x) < 0) xs.push(x);
		if(ys.indexOf(y) < 0) ys.push(y);
		return [x, y, i];
	});

	// xs.sort();
	// ys.sort();

	// xs.forEach(function(x, I){
	// 	var H = coords.filter(function(a){
	// 		return a && a[0] == x;
	// 	});
	// 	if(H.length > 2){
	// 		H.sort(function(a, b){
	// 			return a[1] - b[1]
	// 		});
	// 		H.pop();
	// 		H.shift();
	// 		H.forEach(function(aa){
	// 			coords[aa[2]] = null;
	// 		})
	// 	}
	// });

	// ys.forEach(function(y){
	// 	var H = coords.filter(function(a){
	// 		return a && a[1] == y;
	// 	});
	// 	if(H.length > 2){
	// 		H.sort(function(a, b){
	// 			return a[0] - b[0]
	// 		});
	// 		H.pop();
	// 		H.shift();
	// 		H.forEach(function(aa){
	// 			coords[aa[2]] = null;
	// 		})
	// 	}
	// });

	coords = coords.filter(function(a){
		return !!a;
	});

	return coords;
}

function getImageMosaicData(ctx, gridSize, smooth){
	var w = ctx.canvas.width;
	var h =  ctx.canvas.height;
	var img = ctx.getImageData(0, 0, w, h);
	var data = cleanImageData(img.data, w);

	var kv = {};

	var arr = data.map(function(a){
		var x = ((a[0] / gridSize) >> 0);
		var y = ((a[1] / gridSize) >> 0);
		var k = x + ',' + y;
		if(k in kv){
			return;
		}
		kv[k] = 0;
		return [x, y];
	});
	arr = arr.filter(function(a){
		return !!a;
	})
	smooth && smoothMosaic(arr, gridSize);
	return arr;
}
function getCanvasMosaicData(cvs, gridSize, smooth){
	gridSize = gridSize || Math.pow(2, 4);
	var ctx = cvs.getContext('2d');
	return getImageMosaicData(ctx, gridSize, smooth);
}

function _drawPoint(ctx, a, color, stroke, gridSize){
	ctx.beginPath();
	// ctx.arc(a[0], a[1], 3, 0, Math.PI * 2);
	ctx.rect(a[0] * gridSize, a[1] * gridSize, gridSize, gridSize);
	ctx.closePath();
	if(!stroke){
		ctx.fillStyle = color;
		ctx.fill();
	} else {
		ctx.lineWidth = stroke;
		ctx.strokeStyle = color;
		ctx.stroke();
	}
}
function drawImageMosaic(ctx, gridSize, color, distanceCtx, smooth, stroke){
	distanceCtx = distanceCtx || ctx;
	// console.log(distanceCtx)
	gridSize = gridSize || Math.pow(2, 4);
	color = color || 'rgba(0,0,0,0.3)';
	var mosaic = getImageMosaicData(ctx, gridSize, smooth);

	// distanceCtx.save();
	mosaic.forEach(function(a){
		_drawPoint(distanceCtx, a, color, stroke, gridSize);
		if(a.length > 2){
			for(var i = 0; i < a.length; i++){
				_drawPoint(distanceCtx, a[i], color, stroke, gridSize);
			}
		}
	});
	// distanceCtx.restore();
}

//flag:
// 0:top 1:right 2:bottom 3:left
// 4:tl  5:tr    6:br     7:bl
function findNeighbor(arr, a, flag){
	var t = a.slice(0);
	switch(flag){
		case 1:
			t[0] += 1;
			break;
		case 2:
			t[1] += 1;
			break;
		case 3:
			t[0] -= 1;
			break;
		case 4:
			t[0] -= 1;
			t[1] -= 1;
			break;
		case 5:
			t[0] += 1;
			t[1] -= 1;
			break;
		case 6:
			t[0] += 1;
			t[1] += 1;
			break;
		case 7:
			t[0] -= 1;
			t[1] += 1;
			break;
		default:
			t[1] -= 1;
			break;
	}
	return arr.find(function(A){
		return t[1] === A[1] && t[0] === A[0] && !A[2]
	})
}

function getPattern2(arr, a){
	var l = findNeighbor(arr, a, 3);
	var r = findNeighbor(arr, a, 1);
	var t = findNeighbor(arr, a, 0);
	var b = findNeighbor(arr, a, 2);

	var tl = findNeighbor(arr, a, 4);
	var tr = findNeighbor(arr, a, 5);
	var br = findNeighbor(arr, a, 6);
	var bl = findNeighbor(arr, a, 7);

	var result = [];
	if(l && t){
		return 'lt';
	} else if(r && t){
		return 'rt';
	} else if(l && b){
		return 'lb';
	} else if(r && b){
		return 'rb';
	} else if(l && !r && !t && !b && tr){
		return 'ltr';
	} else if(l && !r && !t && !b && br){
		return 'lbr';
	} else if(!l && r && !t && !b  && tl){
		return 'rtl';
	} else if(!l && r && !t && !b  && bl){
		return 'rbl';

	} else if(!l && !r && !t && b && tl){
		return 'btl';
	} else if(!l && !r && !t && b && tr){
		return 'btr';
	} else if(!l && !r && t && !b  && bl){
		return 'tbl';
	} else if(!l && !r && t && !b  && br){
		return 'tbr';
	}
}

function getPattern(arr, a, size){
	var l = findNeighbor(arr, a, size, 3);
	var r = findNeighbor(arr, a, size, 1);
	var t = findNeighbor(arr, a, size, 0);
	var b = findNeighbor(arr, a, size, 2);
	var result = [];
	if(l && t){
		if(findNeighbor(arr, t, size, 1)){
			result.push('ltr');
			result.push(t);
		} else if(findNeighbor(arr, l, size, 2)) {
			result.push('tlb');
			result.push(l);
		} else {
			result.push('lt');
		}
	} else if(r && t){
		if(findNeighbor(arr, t, size, 3)){
			result.push('rtl');
			result.push(t);
		} else if(findNeighbor(arr, r, size, 2)) {
			result.push('trb');
			result.push(r);
		} else {
			result.push('rt');
		}
	} else if(l && b){
		if(findNeighbor(arr, b, size, 1)){
			result.push('lbr');
			result.push(b);
		} else if(findNeighbor(arr, l, size, 0)){
			result.push('blt');
			result.push(l);
		} else {
			result.push('lb');
		}
	} else if(r && b){
		if(findNeighbor(arr, b, size, 3)){
			result.push('rbl');
			result.push(b);
		} else if(findNeighbor(arr, r, size, 0)){
			result.push('brt');
			result.push(r);
		} else {
			result.push('rb');
		}
	}
	return result;
}

function smoothMosaic(arr){
	return
	size = size || 1;
	var half = size / 1;
	var ins = [];
	var idx = [];

	var h2 = size / 1;
	arr.forEach(function(a, i){


		var pattern = getPattern2(arr, a);
		console.log(pattern)
		switch(pattern){
			case 'rt':
				// arr[i][0] += h2;
				// arr[i][1] -= h2;
				arr.push([arr[i][0] + 1, arr[i][1] - 1, 1]);
				break;
			case 'lt':
				// arr[i][0] -= h2;
				// arr[i][1] -= h2;
				arr.push([arr[i][0] - 1, arr[i][1] - 1, 1]);
				break;
			case 'rb':
				// arr[i][0] += h2;
				// arr[i][1] += h2;
				arr.push([arr[i][0] + 1, arr[i][1] + 1, 1]);
				break;
			case 'lb':
				// arr[i][0] -= h2;
				// arr[i][1] += h2;
				arr.push([arr[i][0] - 1, arr[i][1] + 1, 1]);
				break;
			case 'ltr':
				// arr[i][0] -= h2;
				// arr[i][1] -= h2;
				arr.push([a[0], a[1] - 1, 1]);
				arr.push([a[0] + 1, a[1], 1]);
				// arr.push([a[0], a[1] - 1, 1]);
				break;
			case 'lbr':
				// arr[i][0] -= h2;
				// arr[i][1] -= h2;
				arr.push([a[0] + 1, a[1], 1]);
				arr.push([a[0], a[1] + 1, 1]);
				// arr.push([a[0], a[1] - 1, 1]);
				break;
			case 'tbl':
				// arr[i][0] -= h2;
				// arr[i][1] -= h2;
				arr.push([a[0], a[1] + 1, 1]);
				arr.push([a[0] - 1, a[1], 1]);
				// arr.push([a[0], a[1] - 1, 1]);
				break;
			case 'tbr':
				// arr[i][0] -= h2;
				// arr[i][1] -= h2;
				arr.push([a[0] + 1, a[1], 1]);
				arr.push([a[0], a[1] + 1, 1]);
				// arr.push([a[0], a[1] - 1, 1]);
				break;
		}
		
	});
}


/*
 用户绘制对象基类
 @q canvas标记查询字串
 */
function CanvasPainterBase(q){
	var el = $Q(q);
	el.style.cssText = 'position:absolute;display:block;left:0px;top:0px;';
	var cvs = el.cloneNode(false);
	el.parentNode.appendChild(cvs);
	var state = 0;
	var _ = this;
	_.currentPoint = null;
	var mousedown, mousemove, mouseup;
	addEvent(document, 'mousedown', mousedown = function(e){
		if('button' in e && e.button != 0){
			e.preventDefault();
			return;
		}
		if(e.target.nodeName == 'CANVAS'){
			e.preventDefault();
			e.stopPropagation();
		}
		_.currentPoint  = getSingleEventPostionINT(e);
		state = 1;
		fun(_.onbegin) && _.onbegin(_.currentPoint, state);
	});
	addEvent(document, 'mousemove', mousemove = function(e){
		if(state != 1) return;
		_.currentPoint = getSingleEventPostionINT(e);
		fun(_.onmove) && _.onmove(_.currentPoint, state);
	});
	addEvent(document, 'mouseup', mouseup = function(e){
		if(state === 1){
			_.currentPoint = getSingleEventPostionINT(e);
			_.stop(getSingleEventPostionINT(_.currentPoint));
		}
	});
	_.cvs = cvs;
	_.ctx = cvs.getContext('2d');
	_.target = el;

	_.stop = function(p){
		state = 0;
		fun(_.onstop) && _.onstop(p || _.currentPoint, state);
		_.currentPoint = null;
	};
	_.dispose = function(){
		removeEvent(document, 'mousedown', mousedown);
		removeEvent(document, 'mousemove', mousemove);
		removeEvent(document, 'mouseup', mouseup);
	};
}

/*
 用户绘制对象
 基于基类扩展
 */
function CanvasPainter(q){
	CanvasPainterBase.apply(this, arguments);
	this.path = [];
	// this.helper = this.cvs.cloneNode();
	// this.cvs.parentNode.insertBefore(this.helper, this.cvs);
	this.autoStop = 0;
	this.mosaicSize = Math.pow(2, 4);
	this.debug = false;

	this.runtime = {
		lineWidth: 1,
		strokeStyle: '#000000'
	};

	var stop = this.stop;
	this.stop = function(p){
		clearTimeout(this.$autoStop);
		stop(p);
	};
	var dispose = this.dispose;
	this.dispose = function(){
		clearTimeout(this.$autoStop);
		dispose();
		// this.helper.parentNode &&
		// 	this.helper.parentNode.removeChild(this.helper);
	};

	this.getTargetData = function(smooth){
		return getCanvasMosaicData(this.target, this.mosaicSize, smooth);
	};
}

CanvasPainter.drawImageMosaic = drawImageMosaic;
CanvasPainter.getImageMosaicData = getImageMosaicData;
CanvasPainter.smoothMosaic = smoothMosaic;

window.CanvasPainter = CanvasPainter;

})();